package connection

import (
	"errors"
	"github.com/gorilla/websocket"
	"log"
	"sync"
	"time"
	//"src/ProtoModel"
	"src/model"
)

// websocket 链接
type Ws struct {
	//连接唯一ID
	Id string
	//是否已经登录完成绑定
	Uid string
	//昵称
	Nickname string
	//头像
	Avatar string
	//用户设备id 例如 web,h5,ios
	Did string
	//所在房间号
	RoomNo string
	//已登录用户绑定信息
	User model.ConnUserinfoModel
	//连接
	WsConn    *websocket.Conn
	InChan    chan []byte
	OutChan   chan []byte
	CloseChan chan byte
	Times     int64
	Mutex     sync.Mutex
	IsClosed  bool
}

// 这是更新心跳有效时间函数
func (this *Ws) UpdateTime() {
	this.Times = time.Now().Unix()
}

func (this *Ws) GetConn(wsConn *websocket.Conn) (conn *Ws, err error) {
	conn = &Ws{
		User:      model.ConnUserinfoModel{},
		WsConn:    wsConn,
		InChan:    make(chan []byte, 1000),
		OutChan:   make(chan []byte, 1000),
		CloseChan: make(chan byte, 1),
	}
	//读协程
	go conn.ReadLoop()
	//写协程
	go conn.WriteLoop()
	return conn, err
}

func (conn *Ws) ReadMess() (data []byte, err error) {
	select {
	case data = <-conn.InChan:
	case <-conn.CloseChan:
		err = errors.New("connection is closed")
	}
	return
}

func (conn *Ws) WriteMes(data []byte) (err error) {
	select {
	case conn.OutChan <- data:
	case <-conn.CloseChan:
		err = errors.New("connection is closed")
	}
	return
}

func (conn *Ws) Close() {
	defer conn.Mutex.Unlock()
	//加锁，只能执行一次
	conn.Mutex.Lock()
	if !conn.IsClosed {
		conn.IsClosed = true
		_ = conn.WsConn.Close()
		//本身线程安全，可重入
		close(conn.CloseChan)
	}
}

// 具体实现读消息
func (conn *Ws) ReadLoop() {
	var (
		data []byte
		err  error
	)
	for {
		if _, data, err = conn.WsConn.ReadMessage(); err != nil {
			goto ERR
		}
		select {
		case conn.InChan <- data:
		case <-conn.CloseChan:
			goto ERR
		}
	}
ERR:
	conn.Close()
}

// 具体实现写消息 websocket.BinaryMessage 是二进制类型
func (conn *Ws) WriteLoop() {
	var (
		data []byte
		err  error
	)
	for {
		select {
		case data = <-conn.OutChan:

		case <-conn.CloseChan:
			goto ERR
		}
		if err = conn.WsConn.WriteMessage(websocket.BinaryMessage, data); err != nil {
			log.Println(err)
			goto ERR
		}
	}
ERR:
	conn.Close()
}
